package com.hunliji.monitor.web.facade.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.hunliji.common.exception.WarnException;
import com.hunliji.monitor.manager.api.FeiShuManager;
import com.hunliji.monitor.manager.api.model.dto.ObtainAccessTokenDTO;
import com.hunliji.monitor.manager.api.model.dto.ObtainChatGroupDTO;
import com.hunliji.monitor.manager.api.model.dto.ObtainChatGroupMemberDTO;
import com.hunliji.monitor.manager.api.model.param.FeiShuSendMsgManagerParam;
import com.hunliji.monitor.manager.api.model.param.ObtainAccessTokenManagerParam;
import com.hunliji.monitor.service.api.ChatGroupService;
import com.hunliji.monitor.service.api.model.bo.ChatGroupBO;
import com.hunliji.monitor.service.api.model.param.QueryChatGroupParam;
import com.hunliji.monitor.share.component.ComponentLocator;
import com.hunliji.monitor.share.component.apollo.FeiShuConfig;
import com.hunliji.monitor.share.component.redis.RedisFacade;
import com.hunliji.monitor.share.component.redis.enums.RedisKeyEnum;
import com.hunliji.monitor.share.constants.FeiShuConstants;
import com.hunliji.monitor.share.enums.ReceiveIdTypeEnum;
import com.hunliji.monitor.share.enums.feishu.InterceptEventTypeEnum;
import com.hunliji.monitor.share.utils.DecryptUtil;
import com.hunliji.monitor.share.utils.RedisKeyUtil;
import com.hunliji.monitor.web.config.FeishuIssuePushConfig;
import com.hunliji.monitor.web.facade.FeiShuFacade;
import com.hunliji.monitor.web.model.bo.FeiShuTokenBO;
import com.hunliji.monitor.web.model.param.feishu.InterceptPluginContextParam;
import com.hunliji.monitor.web.model.param.feishu.NotifyParam;
import com.hunliji.monitor.web.model.param.feishu.UrlVerificationNotifyParam;
import com.hunliji.monitor.web.model.vo.NotifyVO;
import com.hunliji.monitor.web.strategy.interception.handler.AbstractFeishuInterceptionHandler;
import com.hunliji.monitor.web.strategy.interception.handler.FeishuInterceptionHandler;
import com.hunliji.monitor.web.strategy.interception.param.FeishuInterceptionContext;
import com.lark.project.core.Config;
import com.lark.project.core.request.RequestOptions;
import com.lark.project.core.token.GlobalTokenManager;
import com.lark.project.service.field.model.FieldValuePair;
import com.lark.project.service.workitem.builder.CreateWorkItemReq;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.tomcat.util.security.MD5Encoder;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 飞书业务处理
 * @author wuchen
 */
@Slf4j
@Service
public class FeiShuFacadeImpl implements FeiShuFacade {
    @Resource
    private FeiShuConfig feiShuConfig;
    @Resource
    private FeiShuManager feiShuManager;
    @Resource
    private RedisFacade redisFacade;
    @Resource
    private ChatGroupService chatGroupService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Resource
    private FeishuIssuePushConfig feishuIssuePushConfig;
    @Resource
    private Config config;

    private static final Map<Integer, FeishuInterceptionHandler> HANDLER_MAP = Maps.newHashMap();
    /**
     * 飞书token的有效时长为2个小时
     */
    private static final Long FEI_SHU_TOKEN_EXPIRES = 60L*60*2;
    /**
     * 类型字段
     */
    private static final String TYPE_FIELD = "type";
    /**
     * 将加密字段解析成一个JSONObject
     * @param encrypt
     * @return
     */
    private JSONObject decryptEncrypt(String encrypt) throws Exception {
        String notifyJson = DecryptUtil.decrypt(encrypt,feiShuConfig.getEncryptKey());
        log.info("decryptEncrypt-解析notify结果,notifyJson:{}",notifyJson);
        return JSON.parseObject(notifyJson);
    }

    /**
     * 处理飞书的 url 校验事件
     * @param jsonObject
     * @return
     */
    private NotifyVO doHandleUrlVerification(JSONObject jsonObject){
        UrlVerificationNotifyParam urlVerificationNotifyParam = JSON.toJavaObject(jsonObject,UrlVerificationNotifyParam.class);
        NotifyVO notifyVO = new NotifyVO();
        notifyVO.setChallenge(urlVerificationNotifyParam.getChallenge());
        return notifyVO;
    }
    /**
     * 处理通知
     * @param notifyParam
     * @return
     */
    @Override
    public NotifyVO handleNotify(NotifyParam notifyParam) throws Exception {
        JSONObject notifyJsonObject = decryptEncrypt(notifyParam.getEncrypt());
        //如果是url 校验类型
        if(FeiShuConstants.NotifyType.URL_VERIFICATION.getCode().equals(notifyJsonObject.getObject(TYPE_FIELD,String.class))){
            return doHandleUrlVerification(notifyJsonObject);
        }
        return null;
    }


    /**
     * 获取token
     * @return
     */
    private ObtainAccessTokenDTO obtainToken(){
        ObtainAccessTokenManagerParam obtainAccessTokenManagerParam = new ObtainAccessTokenManagerParam();
        obtainAccessTokenManagerParam.setAppId(feiShuConfig.getAppId());
        obtainAccessTokenManagerParam.setAppSecret(feiShuConfig.getAppSecret());
        ObtainAccessTokenDTO obtainAccessTokenDTO = feiShuManager.obtainToken(obtainAccessTokenManagerParam);
        log.info("obtainToken-获取token结果:{}",JSON.toJSONString(obtainAccessTokenDTO));
        return obtainAccessTokenDTO;
    }

    /**
     * 获取gitlab的token
     * @return
     */
    private ObtainAccessTokenDTO gitLabObtainToken(){
        ObtainAccessTokenManagerParam obtainAccessTokenManagerParam = new ObtainAccessTokenManagerParam();
        obtainAccessTokenManagerParam.setAppId(feiShuConfig.getGitLabAppId());
        obtainAccessTokenManagerParam.setAppSecret(feiShuConfig.getGitLabAppSecret());
        ObtainAccessTokenDTO obtainAccessTokenDTO = feiShuManager.obtainToken(obtainAccessTokenManagerParam);
        log.info("gitLabObtainToken-获取token结果:{}",JSON.toJSONString(obtainAccessTokenDTO));
        return obtainAccessTokenDTO;
    }

    /**
     * token 的处理
     */
    @Override
    public void handleToken() {
        //先获取token,然后把这个token加到缓存中去
        ObtainAccessTokenDTO obtainAccessTokenDTO = obtainToken();
        FeiShuTokenBO feiShuTokenBO = ComponentLocator.getConverter().convert(obtainAccessTokenDTO,FeiShuTokenBO.class);
        feiShuTokenBO.setToken(obtainAccessTokenDTO.getTenantAccessToken());
        String redisKey = RedisKeyUtil.getKey(RedisKeyEnum.FEI_SHU_TOKEN);
        redisFacade.set(redisKey,JSON.toJSONString(feiShuTokenBO), Objects.nonNull(obtainAccessTokenDTO.getExpire()) ? obtainAccessTokenDTO.getExpire().longValue() - 30 : FEI_SHU_TOKEN_EXPIRES);
    }

    /**
     * 处理群组的存储
     * @param obtainChatGroupDTO
     */
    private void doSaveChatGroup(ObtainChatGroupDTO obtainChatGroupDTO){
        QueryChatGroupParam queryChatGroupParam = new QueryChatGroupParam();
        queryChatGroupParam.setFeiShuChatId(obtainChatGroupDTO.getChatId());
        List<ChatGroupBO> chatGroupBOList = chatGroupService.query(queryChatGroupParam);
        //如果这个数据不存在，则执行存储
        if(CollectionUtils.isEmpty(chatGroupBOList)){
            ChatGroupBO chatGroupBO = handleChatGroupEntity(null,obtainChatGroupDTO);
            chatGroupService.save(chatGroupBO);
        }else {
            ChatGroupBO chatGroupBO = chatGroupBOList.get(0);
            chatGroupBO = handleChatGroupEntity(chatGroupBO,obtainChatGroupDTO);
            chatGroupService.update(chatGroupBO);
        }

    }

    /**
     * 将群组信息处理成存储实体
     * @param chatGroupBO
     * @param obtainChatGroupDTO
     * @return
     */
    private ChatGroupBO handleChatGroupEntity(ChatGroupBO chatGroupBO,ObtainChatGroupDTO obtainChatGroupDTO){
        if(Objects.isNull(chatGroupBO)){
            chatGroupBO = new ChatGroupBO();
        }
        chatGroupBO.setGroupChatCode(obtainChatGroupDTO.getChatId());
        chatGroupBO.setName(obtainChatGroupDTO.getName());
        chatGroupBO.setDescription(obtainChatGroupDTO.getDescription());
        chatGroupBO.setAvatar(obtainChatGroupDTO.getAvatar());
        chatGroupBO.setFeiShuChatId(obtainChatGroupDTO.getChatId());
        return chatGroupBO;
    }

    /**
     * 处理飞书群组的更新
     */
    @Override
    public void handleChatGroup() {
        String redisKey = RedisKeyUtil.getKey(RedisKeyEnum.FEI_SHU_TOKEN);
        String value = redisFacade.get(redisKey);
        if(StringUtils.isBlank(value)){
            log.warn("handleChatGroup-无法获取到token");
        }else {
            FeiShuTokenBO feiShuTokenBO = JSON.parseObject(value,FeiShuTokenBO.class);
            //先获取
            List<ObtainChatGroupDTO> obtainChatGroupDTOList = feiShuManager.obtainChatGroupAll(feiShuTokenBO.getToken());
            for (ObtainChatGroupDTO obtainChatGroupDTO:obtainChatGroupDTOList){
                doSaveChatGroup(obtainChatGroupDTO);
            }
        }
    }

    /**
     * 处理飞书群组的成员
     */
    @Override
    public void handleChatGroupMember() {
        String redisKey = RedisKeyUtil.getKey(RedisKeyEnum.FEI_SHU_TOKEN);
        String value = redisFacade.get(redisKey);
        if(StringUtils.isBlank(value)){
            log.warn("handleChatGroup-无法获取到token");
        }else {
            FeiShuTokenBO feiShuTokenBO = JSON.parseObject(value,FeiShuTokenBO.class);
            //先获取
            //先获取全部的群组
            QueryChatGroupParam queryChatGroupParam = new QueryChatGroupParam();
            List<ChatGroupBO> chatGroupBOList = chatGroupService.query(queryChatGroupParam);
            log.info("handleChatGroupMember-处理飞书群组成员，获取群组，size:{}",chatGroupBOList.size());
            for (ChatGroupBO chatGroupBO:chatGroupBOList){
                handleOneChatMember(chatGroupBO,feiShuTokenBO.getToken());
            }
        }

    }

    /**
     * 处理一个群组的成员
     * @param chatGroupBO
     */
    private void handleOneChatMember(ChatGroupBO chatGroupBO,String token){
        //查询全部的飞书中的成员
        List<ObtainChatGroupMemberDTO> chatGroupMemberDTOList = feiShuManager.obtainChatGroupMemberAll(token,chatGroupBO.getGroupChatCode());
        //然后查询当前数据库中的数据，然后转成一个map
    }

    /**
     * 处理飞书通知事件
     * @param receiveSign：接收方标记
     * @param content：接收的内容
     */
    @Override
    public void handleNotify(String receiveSign, String content) {
        String redisKey = RedisKeyUtil.getKey(RedisKeyEnum.FEI_SHU_TOKEN);
        String value = redisFacade.get(redisKey);
        if(StringUtils.isBlank(value)){
            log.warn("handleNotify-无法获取到token");
        }else {
            FeiShuTokenBO feiShuTokenBO = JSON.parseObject(value,FeiShuTokenBO.class);
            //去发送消息
            feiShuManager.sendChatGroupMsg(receiveSign,content,feiShuTokenBO.getToken());
            //生成缺陷
            createDefect(null,content);
        }
    }

    public String getAccessToken(){
        String redisKey = RedisKeyUtil.getKey(RedisKeyEnum.FEI_SHU_TOKEN);
        String feiShuToken = redisFacade.get(redisKey);
        if(StringUtils.isBlank(feiShuToken)){
            //先获取token,然后把这个token加到缓存中去
            ObtainAccessTokenDTO obtainAccessTokenDTO = obtainToken();
            FeiShuTokenBO feiShuTokenBO = new FeiShuTokenBO();
            feiShuTokenBO.setToken(obtainAccessTokenDTO.getTenantAccessToken());
            redisFacade.set(redisKey, JSON.toJSONString(feiShuTokenBO),
                Objects.nonNull(obtainAccessTokenDTO.getExpire()) ? obtainAccessTokenDTO.getExpire().longValue() - 30 : FEI_SHU_TOKEN_EXPIRES);
            return obtainAccessTokenDTO.getTenantAccessToken();
        }
        FeiShuTokenBO feiShuTokenBO = JSON.parseObject(feiShuToken,FeiShuTokenBO.class);
        return Optional.ofNullable(feiShuTokenBO).map(FeiShuTokenBO::getToken).orElse(null);
    }

    public String getGitLabAccessToken(){
        String redisKey = RedisKeyUtil.getKey(RedisKeyEnum.GITLAB_FEI_SHU_TOKEN);
        String feiShuToken = redisFacade.get(redisKey);
        if(StringUtils.isBlank(feiShuToken)){
            //先获取token,然后把这个token加到缓存中去
            ObtainAccessTokenDTO obtainAccessTokenDTO = gitLabObtainToken();
            FeiShuTokenBO feiShuTokenBO = new FeiShuTokenBO();
            feiShuTokenBO.setToken(obtainAccessTokenDTO.getTenantAccessToken());
            redisFacade.set(redisKey, JSON.toJSONString(feiShuTokenBO),
                Objects.nonNull(obtainAccessTokenDTO.getExpire()) ? obtainAccessTokenDTO.getExpire().longValue() - 30 : FEI_SHU_TOKEN_EXPIRES);
            return obtainAccessTokenDTO.getTenantAccessToken();
        }
        FeiShuTokenBO feiShuTokenBO = JSON.parseObject(feiShuToken,FeiShuTokenBO.class);
        return Optional.ofNullable(feiShuTokenBO).map(FeiShuTokenBO::getToken).orElse(null);
    }


    @Override
    public void handleInterceptionNotify(InterceptPluginContextParam param) {
        if (param.getEventType() == null) {
            throw new WarnException("缺少事件类型");
        }

        FeishuInterceptionHandler handler = HANDLER_MAP.get(param.getEventType());
        if (handler == null) {
            log.info("暂无处理此类事件的handler");
            return;
        }
        FeishuInterceptionContext context = new FeishuInterceptionContext();
        context.setParam(param);
        handler.process(context);
    }

    @Override
    public void delayCancel(Long workItemId, Integer type) {
        String key = getFeishuDelayKey(workItemId, type);
        stringRedisTemplate.opsForValue().set(key, "1", 1, TimeUnit.DAYS);


        FeiShuSendMsgManagerParam feiShuSendMsgManagerParam = new FeiShuSendMsgManagerParam();
        feiShuSendMsgManagerParam.setMsgType("text");
        JSONObject content = new JSONObject();
        content.put("text", "缺陷跟进成功");
        feiShuSendMsgManagerParam.setContent(content.toJSONString());
        feiShuSendMsgManagerParam.setReceiveId(feishuIssuePushConfig.getChatId());
        feiShuSendMsgManagerParam.setReceiveIdType(ReceiveIdTypeEnum.CHAT_ID.getCode());
        feiShuManager.sendMsg(feiShuSendMsgManagerParam, getAccessToken());
    }

    private String getFeishuDelayKey(Long workItemId, Integer type) {
        return "FeishuDelay:" + workItemId + "_" + type;
    }


    public static void register(InterceptEventTypeEnum interceptEventTypeEnum, FeishuInterceptionHandler feishuInterceptionHandler) {
        HANDLER_MAP.put(interceptEventTypeEnum.getEventType(), feishuInterceptionHandler);
    }

    @Override
    public void createDefect(String title,String content){
        try{
            if(!feiShuConfig.getCreateItemOpen()){
                log.warn("开关已关闭");
                return ;
            }
            //String template="{\"project_key\":\"65b08cc842b33609ca3451a5\",\"work_item_type_key\":\"issue\",\"name\":\"自动生成缺陷\",\"template_id\":709775,\"field_value_pairs\":[{\"field_key\":\"description\",\"field_value\":\"2333活动四已守护神呢\"},{\"field_key\":\"priority\",\"field_value\":{\"label\":\"P2\",\"value\":\"2\"}},{\"field_key\":\"severity\",\"field_value\":{\"label\":\"一般\",\"value\":\"3\"}},{\"field_key\":\"issue_stage\",\"field_value\":{\"label\":\"线上阶段\",\"value\":\"stage_online\"}},{\"field_key\":\"bug_classification\",\"field_value\":{\"label\":\"Server\",\"value\":\"server\"}}]}";
            final String nameKey="name";
            final String fieldValuePairsKey="field_value_pairs";
            final String descriptionKey="description";
            final String fieldValueKey="field_value";
            final String projectKey="project_key";
            Map<String, Object> paramMap=JSON.parseObject(feiShuConfig.getCreateItemTemplate(),HashMap.class);
            Optional.of(title).ifPresent(v->paramMap.put(nameKey,title));
            paramMap.entrySet().stream().forEach(v->{
                if(v.getKey().equals(fieldValuePairsKey)){
                    List<HashMap> subParams=JSON.parseArray(v.getValue().toString(),HashMap.class);
                    for(Map<String,Object> map:subParams){
                        for(Map.Entry entry:map.entrySet()){
                            if(descriptionKey.equals(entry.getValue())){
                                map.put(fieldValueKey,content);
                                break;
                            }
                        }
                    }
                    paramMap.put(v.getKey(),subParams);
                }
            });
            if(hasGenerateDefect(paramMap)){
                log.warn("createDefect#已生成过缺陷,paramMap:{}",JSON.toJSONString(paramMap));
                return ;
            }
            String accessToken = GlobalTokenManager.getTokenManager().getAccessTokenThenCache(config);
            log.warn("accessToken:{}",accessToken);
            feiShuManager.createItemV2(paramMap,accessToken,paramMap.get(projectKey).toString());
        }catch (Exception e){
            log.error("createDefect#error content:{}",content,e);
        }
    }

    private Boolean hasGenerateDefect(Map<String, Object> paramMap){
        try {
            String StrKey= "createDefect:".concat(Base64.getEncoder().encodeToString(JSONObject.toJSONString(paramMap).getBytes("utf-8")));
            String value = redisFacade.get(StrKey);
            if(StringUtils.isNotBlank(value)){
                return true;
            }
            redisFacade.set(StrKey,"1",4*60*60);
            return false;
        } catch (UnsupportedEncodingException e) {
            log.error("hasGenerateDefect#error",e);
        }
        return false;
    }
}
